﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using System.Diagnostics;
using System.Windows.Threading;
using System.Threading;
using System.IO;
using System.ComponentModel;
using System.Text.RegularExpressions;
using Microsoft.Practices.Composite.Presentation.Commands; 

using avlib_wrapper;

namespace dmm
{
    /// <summary>
    /// Interaction logic for VideoPlayer.xaml
    /// </summary>
    public partial class VideoPlayer : UserControl,RecorderObserver
    {
        //public static RoutedUICommand CmdPlayVideo = new RoutedUICommand("Add Videos", "PlayVideo", typeof(VideoPlayer));
        /*public static RoutedUICommand CmdStopVideo = new RoutedUICommand("Record a Video", "StopVideo", typeof(VideoPlayer));
        public static RoutedUICommand CmdShotVideo = new RoutedUICommand("Delete selected Video", "ShotVideo", typeof(VideoPlayer));
        public static RoutedUICommand CmdCropVideo = new RoutedUICommand("Play the Video", "CropVideo", typeof(VideoPlayer));
        public static RoutedUICommand CmdTrimStartVideo = new RoutedUICommand("Convert Selected Videos", "TrimStartVideo", typeof(VideoPlayer));
        public static RoutedUICommand CmdTrimEndVideo = new RoutedUICommand("Convert Selected Videos", "TrimEndVideo", typeof(VideoPlayer));
        public static RoutedUICommand CmdUnTrimVideo = new RoutedUICommand("Convert Selected Videos", "UnTrimVideo", typeof(VideoPlayer));*/
        public DelegateCommand<object> CommandPlay{get;set;}
        public DelegateCommand<object> CommandStop { get; set; }
        public DelegateCommand<object> CommandShot { get; set; }
        public DelegateCommand<object> CommandCrop { get; set; }
        public DelegateCommand<object> CommandTrimStart { get; set; }
        public DelegateCommand<object> CommandTrimEnd { get; set; }
        public DelegateCommand<object> CommandUntrim { get; set; }
        public DelegateCommand<object> CommandConvert { get; set; }

        private MiniWindow mini_win = null;
        private Window main_win = null;
        public Window MainWindow
        {
            get { return main_win; }
            set { main_win = value; }

        }
        private Grid parentCtrl = null;


        public static readonly RoutedEvent VideoCommitedEvent = EventManager.RegisterRoutedEvent("VideoCommited", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(VideoPlayer));
        public enum PlayerStatus
        {
            EMPTY, //not initialized,no video specfied
            PLAYING,
            RECORDING,
            PAUSED,
            STOPPED,
        };

        public enum PlayerDisplayMode
        {
            MINI,
            NORMAL,
            FULLSCREEN,
        };

        private PlayerDisplayMode displayMode = PlayerDisplayMode.NORMAL;
        public PlayerDisplayMode DisplayMode
        {
            get{ return displayMode;}
            set
            {
                if (value == displayMode) return;
                displayMode = value;

                if (displayMode == PlayerDisplayMode.MINI)
                {
                    adv_toolbar.Visibility = Visibility.Hidden;
                    player_progress.Visibility = Visibility.Hidden;

                    if (mini_win == null)
                    {
                        mini_win = new MiniWindow();
                       
                        mini_win.SizeChanged += new SizeChangedEventHandler(
                            delegate(Object s, SizeChangedEventArgs e1) {
                                try
                                {
                                    if (Window.GetWindow(this) != null)
                                        this.adjust_layout();
                                }
                                catch (Exception e) {
                                    Debug.Write(e);
                                };
                                
                            });
                        mini_win.LocationChanged += new EventHandler(
                            delegate(object s, EventArgs e1) {
                                try{
                                    if(Window.GetWindow(this) != null)
                                        this.adjust_layout();
                                }
                                catch (Exception e)      
                                {
                                    Debug.Write(e);
                                }
                            });
                        mini_win.Closing += new CancelEventHandler(
                            delegate(Object s, CancelEventArgs arg)
                            {
                                if (Window.GetWindow(this) == mini_win)
                                {
                                    this.DisplayMode = PlayerDisplayMode.NORMAL;
                                    arg.Cancel = true;
                                }
                            });
                    }
                  

                    Grid.SetRowSpan(video_preview_frame, 2);
                    main_win = Window.GetWindow(this);
                    main_win.Hide();

                    parentCtrl = VisualTreeHelper.GetParent(this) as Grid;
                    if (parentCtrl != null)
                        parentCtrl.Children.Remove(this);
                    mini_win.container.Children.Add(this);

                    wndPreview.Show();

                    //video_preview_frame.Width = 240;
                    //video_preview_frame.Height = 240 * video.Height/video.Width;
                    //mini_win.SizeToContent = SizeToContent.WidthAndHeight;
                    mini_win.Left = 0;
                    mini_win.Top = 0;
                    mini_win.Width = 240;
                    mini_win.Height = 210;

                   
                    mini_win.Show();
                   
                }


                if (displayMode == PlayerDisplayMode.NORMAL)
                {
                    mini_win.Hide();

                    Grid.SetRowSpan(video_preview_frame, 1);
                    player_progress.Visibility = Visibility.Visible;
                    adv_toolbar.Visibility = Visibility.Visible;

                    mini_win.container.Children.Remove(this);
                    parentCtrl.Children.Add(this);
                    Grid.SetRow(this, 0);

                    this.ShowPlayerWindow = bShowPlayer;
                    main_win.Show();
                   
                }
                
            }
        }
       
        private bool bFullScreen = false;
        private Video video = null;

        private Recorder recorder = null;
        public Recorder Recorder
        {
            get
            {
                return recorder;
            }
            set
            {
                if(recorder != null){
                    recorder.unregister_observer(this);
                }
                recorder = value;
                if(recorder != null)
                    recorder.register_observer(this);
               
            }
        }
       
        private Window wndPreview = new Window();
        private uint player_id = 0;
        private ExecutorWrapper exe = new ExecutorWrapper();
        private PlayerStatus status = PlayerStatus.PLAYING;
        private PlayerOutCallback callback = null;

        private RegionSelector selector = new RegionSelector();

        bool is_seeking = false;

        bool bShowPlayer = false;
        public bool ShowPlayerWindow
        {
            set
            {
                bShowPlayer = value;
                if (value)
                {
                    if (video != null && video.is_cropped())
                    {
                        if(selector != null)selector.Show();
                    }
                    
                    wndPreview.Show();
                }
                else
                {
                    if (selector != null) selector.Hide();
                    wndPreview.Hide();
                }

            }
            get
            {
                return bShowPlayer;
            }
        }
        public bool IsRecording
        {
            get {return  Status == PlayerStatus.RECORDING; }
        }

        public PlayerStatus Status
        {
            get { return status; }
            set
            {
                if (status == value ) return;
                Debug.Write(status + "------------------>" + value + "\n");
                status = value;


                CommandPlay.RaiseCanExecuteChanged();
                CommandStop.RaiseCanExecuteChanged();
                CommandShot.RaiseCanExecuteChanged();
                CommandTrimStart.RaiseCanExecuteChanged();
                CommandTrimEnd.RaiseCanExecuteChanged();
                CommandUntrim.RaiseCanExecuteChanged();
                CommandCrop.RaiseCanExecuteChanged();
                CommandConvert.RaiseCanExecuteChanged();

                try
                {
                   // string filename = "pack://siteoforigin:,,,/icons/play.ico";
                    string filename = "icons/play.ico";
                    if (Status == PlayerStatus.PLAYING)
                    {
                        //filename = "pack://siteoforigin:,,,/icons/pause.ico";
                        filename = "icons/pause.ico";
                    }
                    BitmapImage img = new BitmapImage();
                    img.BeginInit();
                    img.UriSource = new Uri(filename, UriKind.RelativeOrAbsolute);
                    img.EndInit();
                    play_icon.Source = img;
                }
                catch (Exception e)
                {
                    Debug.Write(e);
                }
            }
        }

        public VideoPlayer()
        {
            ShowPlayerWindow = true;
            selector.ShowCloser = false;

            CommandPlay = new DelegateCommand<object>(onPlay, canPlay);
            CommandStop = new DelegateCommand<object>(onStop, canStop);
            CommandShot = new DelegateCommand<object>(onShot, canShot);
            CommandCrop = new DelegateCommand<object>(onCrop, canCrop);
            CommandTrimStart = new DelegateCommand<object>(onTrimStart, canTrimStart);
            CommandTrimEnd = new DelegateCommand<object>(onTrimEnd, canTrimEnd);
            CommandUntrim = new DelegateCommand<object>(onUnTrim, canUnTrim);
            CommandConvert = new DelegateCommand<object>(onConvert, canConvert);

            InitializeComponent();


            btnPlay.Command = CommandPlay;
            btnStop.Command = CommandStop;
            btnShot.Command = CommandShot;
            btnCrop.Command = CommandCrop;
            btnTrimStart.Command = CommandTrimStart;
            btnTrimEnd.Command = CommandTrimEnd;
            btnUnTrim.Command = CommandUntrim;
            btnConvert.Command = CommandConvert;

            // CommandPlay.RaiseCanExecuteChanged();

            wndPreview.WindowStyle = WindowStyle.None;
            wndPreview.ResizeMode = ResizeMode.NoResize;
            wndPreview.MouseDoubleClick += toggle_mini;

           
            player_progress.IsMoveToPointEnabled = true;
            volume_slider.IsMoveToPointEnabled = true;

            selector.RegionChanged += onRegionChanged;

            Status = PlayerStatus.EMPTY;

            callback = new PlayerOutCallback(this);
        }


        #region  main interface
      
        private void adjust_duration()
        {
            player_progress.Minimum = 0;
            player_progress.Maximum = video.Duration;
            player_progress.TickFrequency = 1.0;

            if (video.is_trimed())
            {
                player_progress.SelectionStart = video.VideoSetting.trim_start;
                player_progress.SelectionEnd = video.VideoSetting.trim_end;
            }
            else
            {
                player_progress.SelectionStart = player_progress.SelectionEnd = 0;
            }
        }

        private void adjust_crop()
        {
            if (video.is_cropped())
            {
               
                selector.set_bound(wndPreview.Left, wndPreview.Top, wndPreview.Width, wndPreview.Height);
                selector.Region = video.VideoSetting.crop_rect;
                selector.Show();
            }
            else
            {
                selector.Hide();
            }
        }

        public void onVideoPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (sender == theVideo)
            {
                if (e.PropertyName == "AUDIO_STREAM")
                {
                    as_selector.Items.Refresh();
                }

                if (e.PropertyName == "SUBTITLE_STREAM")
                {
                    ts_selector.Items.Refresh();
                }
            }
        }

        public void play()
        {

            if (video == null)
            {
                MessageBox.Show(@"Please select a video to play");
                return;
            }
            if (IsRecording)
            {
                stop();
            }
            if (player_id != 0)
            {
                stop();
            }

            adjust_duration();
            adjust_crop();
            adjust_layout();
            IntPtr windowHandle = new System.Windows.Interop.WindowInteropHelper(wndPreview).Handle;            
            double volume = volume_slider.Value;
            string program = Options.Instance.PlayerPath;
            string param = String.Format("-osdlevel 0  -slave -identify -nofs  -nomouseinput -vo direct3d  -nocache -idx -sub-fuzziness 1 -vf scale,screenshot  -wid {0} -af volnorm -framedrop -nokeepaspect  \"{1}\" -volume {2} ", windowHandle.ToInt32(), video.Path, volume);
            //string param = String.Format("-osdlevel 0  -slave -identify -nocache -vf screenshot  -wid {0} -af volnorm -framedrop -nokeepaspect -sid 1  \"{1}\" -volume {2} -vo direct3d  ", windowHandle.ToInt32(), video.Path, volume);

            param += @" -ass -embeddedfonts ";
           
            if (video.is_trimed())
            {
                param = "-edl "+Options.Instance.EdlOutPath+@"\edl" + video.ID + ".txt " + param;
            }

           

            as_selector.ItemsSource = theVideo.audio_streams;
            as_selector.Items.Refresh();
            ts_selector.ItemsSource = theVideo.sub_streams;
            ts_selector.Items.Refresh();

            as_selector.SelectedValue = theVideo.selected_aid;
            ts_selector.SelectedValue = theVideo.selected_tid;
            if (theVideo.selected_aid != -1)
            {
                param = "-aid " + theVideo.selected_aid + " " + param;
            }
            if (theVideo.selected_tid != -1)
            {
                param = "-sid " + theVideo.selected_tid + " " + param;
            }
            

            player_id = exe.exec(program, param,Options.Instance.WorkPath, callback, new IntPtr(0), true);
            Status = PlayerStatus.PLAYING;

            theVideo.PropertyChanged += onVideoPropertyChanged;
        }

        public void shotscreen()
        {
            if (player_id != 0)
            {
                 exe.command(player_id, "screenshot 0\n");
                 if(Status == PlayerStatus.PAUSED)
                    exe.command(player_id, "pause\n");
            }
        }

        private string shotedThumbnail = "";

        private void DoReplaceThumbnail()
        {
            Trace.Write("enter Screen shot\n");
            //string exe = (new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase)).AbsolutePath;
           // string path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

            string path = Options.Instance.WorkPath;
            string oldfilename = path  + shotedThumbnail;
            string newfilename = null;

            int ntry = 0;
            while (ntry<3)
            {
                //wait for file saved succefully
                try
                {

                    FileStream fs = File.Open(oldfilename, FileMode.Open, FileAccess.Write, FileShare.None);
                    fs.Close();
                    newfilename = Options.newUniqueFile(Options.Instance.ThumbnailOutPath, video.FileNameNoExt, "png");
                    File.Copy(oldfilename, newfilename, true);
                    break;

                }
                catch (Exception e)
                {
                    Trace.Write(e);
                    ntry++;
                    Thread.Sleep(50);
                }
            }

            try
            {
                File.Delete(oldfilename);
                if (newfilename != null)
                {
                    this.Dispatcher.BeginInvoke((Action)(() =>
                    {
                        video.Thumbnail = newfilename;
                    }));
                    Trace.Write("leave Screen shor\n");
                }
            }
            catch (Exception e)
            {
                Debug.Write(e);
            }
        }

        public void screen_shot(string filename)
        {
            //this should be async,use a new thread to replace the thumbnail to 
            //prevent deadlock
            shotedThumbnail = filename;
            new Thread(new ThreadStart(DoReplaceThumbnail)).Start();
        }

        public void pause()
        {
            if (player_id != 0)
            {
                exe.command(player_id, "pause\n");
                
                if (Status == PlayerStatus.PAUSED)
                    Status = PlayerStatus.PLAYING;
            }
        }

        private void stop_player(uint pid){
            //to make sure the player exit,we command,then stop/kill

            Thread th = new Thread(new ParameterizedThreadStart(delegate(object obj)
            {
                uint id = (uint)obj ;
                exe.command(id, "stop\n");
                //have a rest
                Thread.Sleep(500);
                exe.stop(id);
                
            }));
            th.Start(pid as object);
        }

        public void stop()
        {
            if(IsRecording)
            {
                recorder.stop();

            }
            else if (player_id != 0)
            {
               // exe.command(player_id, "stop\n");
                stop_player(player_id);
            }
        }

        public void close()
        {
            if(DisplayMode != PlayerDisplayMode.NORMAL)
                   DisplayMode = PlayerDisplayMode.NORMAL;
            stop();
            if (player_id != 0)
            {
                exe.stop(player_id);
            }
        }


       
        public Video theVideo
        {
            get
            {
                return video;
            }
            set
            {
                //btnPlay.IsEnabled = true;
                //btnConvert.IsEnabled = true;
                video = value;
                adjust_layout();
                CommandPlay.RaiseCanExecuteChanged();
               
            }
        }
        #endregion


        #region private helper functions

        public IntPtr get_window_handle()
        {
            return new System.Windows.Interop.WindowInteropHelper(wndPreview).Handle;
        }
        private void seek()
        {
            double prog = player_progress.Value;
            exe.command(player_id, "seek " + String.Format("{0:0.00}", prog) + " 2\n");
            Debug.Write("\nSeek to " + String.Format("{0:0.00}", prog) + "\n");
        }

        private void seek_pause()
        {
            double prog = player_progress.Value;
            exe.command(player_id, "pausing seek " + String.Format("{0:0.00}", prog) + " 2\n");
            Debug.Write("\nSeek to " + String.Format("{0:0.00}", prog) + "\n");
        }

        public void set_play_progress(double second)
        {
            
            if (is_seeking) return;

            if (IsRecording)
            {
                player_progress.Maximum = second;
                
            }
            player_progress.Value = second - video.StartTime;

            int s = (int)second;
            int p = (int)((second - s) * 1000);
            int h = s / 3600;
            s -= h * 3600;
            int m = s / 60;
            s -= m * 60;

            play_time.Text = String.Format("{0:D2}:{1:D2}:{2:D2}.{3:D3}", h, m, s, p);
        }

        public void adjust_layout()
        {
            if (PresentationSource.FromVisual(video_preview_frame) != null)
            {
                Debug.Write("enter adjust layout\n");
                int vw = 1;
                int vh = 1;
                if (video != null)
                {
                    vw = video.Width;
                    vh = video.Height;
                }

                
                double frame_width = video_preview_frame.ActualWidth - video_preview_frame.BorderThickness.Left - video_preview_frame.BorderThickness.Right;
                double frame_height = video_preview_frame.ActualHeight - video_preview_frame.BorderThickness.Top - video_preview_frame.BorderThickness.Bottom;
                if (bFullScreen)
                {
                    frame_width = (int)System.Windows.SystemParameters.PrimaryScreenWidth;
                    frame_height = (int)System.Windows.SystemParameters.PrimaryScreenHeight;
                }
                double scale1 = frame_width / frame_height;
                double scale2 = (double)vw / vh;


                double w = frame_width;
                double h = frame_height;
                if (scale1 > scale2)
                {
                    w = scale2 * h;
                }
                else
                {
                    h = w / scale2;
                }

                //reserve one pixel border?
                //w -= 2;
                //h -= 2;

              
                Point point = video_preview_frame.PointToScreen(new Point(0, 0));

                wndPreview.Left = point.X + video_preview_frame.BorderThickness.Left + (frame_width - w) / 2;
                wndPreview.Top = point.Y + video_preview_frame.BorderThickness.Top + (frame_height - h) / 2;
                if (bFullScreen)
                {
                    wndPreview.Left = (frame_width - w) / 2;
                    wndPreview.Top = (frame_height - h) / 2;
                }
                if (w < 1) w = 1;
                if (h < 1) h = 1;

                wndPreview.Width = w;
                wndPreview.Height = h;
                if (bFullScreen)
                {
                    wndPreview.Left = 0;
                    wndPreview.Top = 0;
                    wndPreview.Width = frame_width;
                    wndPreview.Height = frame_height;
                }


                IntPtr windowHandle = get_window_handle();
                
                if (recorder != null)
                    recorder.SetPreviewWindow(windowHandle);

                if (selector != null)
                    selector.set_bound(wndPreview.Left, wndPreview.Top, w, h);
            }
            Debug.Write("leave adjust layout\n");
        }

        public void toggle_fullscreen(object sender, RoutedEventArgs e)
        {
            /*if (player_id != 0)
            {
                exe.command(player_id, "vo_fullscreen 1\n");
            }*/

           
            /*bFullScreen = !bFullScreen;
            adjust_layout();*/
        }

        public void toggle_mini(object sender, RoutedEventArgs e)
        {
            if(DisplayMode == PlayerDisplayMode.NORMAL)
                DisplayMode =PlayerDisplayMode.MINI;
            else if (DisplayMode == PlayerDisplayMode.MINI)
                DisplayMode = PlayerDisplayMode.NORMAL;

        }


        private void onRegionChanged(object sender, RoutedEventArgs e)
        {
            video.crop(selector.Region.Left, selector.Region.Top, selector.Region.Width, selector.Region.Height);
        }

        public bool canPlay(object sender)
        {
            return  ((theVideo != null)&& (status != PlayerStatus.RECORDING));
        }
        private bool canStop(object sender)
        {
            return ((theVideo!=null) && status != PlayerStatus.EMPTY);
        }
        private bool canShot(object sender)
        {
            return ((theVideo != null) && (status == PlayerStatus.PLAYING || status == PlayerStatus.PAUSED));
        }
        private bool canCrop(object sender)
        {
            return (status == PlayerStatus.PLAYING || status == PlayerStatus.PAUSED);
        }
        private bool canTrimStart(object obj)
        {
            return ((theVideo != null) && (status != PlayerStatus.RECORDING));
        }
        private bool canTrimEnd(object obj)
        {
            return ((theVideo != null) && (status != PlayerStatus.RECORDING));
        }
        private bool canUnTrim(object obj)
        {
            return ((theVideo != null) && (status != PlayerStatus.RECORDING) && theVideo.is_trimed());
        }
        private bool canConvert(object obj)
        {
            return ((theVideo != null) && (status != PlayerStatus.RECORDING));
        }

        private void onLoaded(object sender, RoutedEventArgs e)
        {
            //WindowStyle="None"  ResizeMode="NoResize"
            if (DisplayMode == PlayerDisplayMode.NORMAL)
                wndPreview.Owner = main_win;
            else if (DisplayMode == PlayerDisplayMode.MINI)
                wndPreview.Owner = mini_win;
            
            adjust_layout();
            wndPreview.ShowInTaskbar = false;

            //wndPreview.Topmost = true;
            wndPreview.Show();
            //wndPreview.Topmost = false;
            wndPreview.Activate();

            if (recorder != null)
                recorder.Setting.PreviewWnd = get_window_handle();
        }

        #endregion

        #region event process
        public void onPlayerExit(uint id, IntPtr code)
        {
            if (id == player_id)
            {
                player_id = 0;
                Status = PlayerStatus.EMPTY;
                player_progress.Value = 0;
               
            }
            Debug.Write("mplayer exited\n");

        }

       

        private void onPlay(object sender)
        {
            switch (status)
            {
                case PlayerStatus.EMPTY:
                case PlayerStatus.STOPPED:
                    play();
                    break;
                case PlayerStatus.PLAYING:
                case PlayerStatus.PAUSED:
                    pause();
                    break;
            }
        }

        private void onUnloaded(object sender, EventArgs e)
        {
            //stop();
        }
        
        private void onStop(object sender)
        {
            stop();
        }

        private void onShot(object sender)
        {
            shotscreen();
        }

        private void onThumbDragStarted(object sender, DragStartedEventArgs e)
        {
            // seeking = true;
        }

        private void onThumbDragCompleted(object sender, DragCompletedEventArgs e)
        {
            // seek();
            //seeking = false;
        }

        private void onSliderPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //base.OnPreviewMouseLeftButtonDown(e);
            is_seeking = true;
            if (status == PlayerStatus.PLAYING)
                seek();
            else if (status == PlayerStatus.PAUSED)
                seek_pause();
            else if (status == PlayerStatus.EMPTY)
            {
                play();
                seek_pause();
            }
        }


        private void onSliderPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            //base.OnPreviewMouseLeftButtonUp(e);
            is_seeking = false;
        }
        #endregion


        private void onSizeChanged(object sender, SizeChangedEventArgs e)
        {
            adjust_layout();
        }

        private void change_volume_icon()
        {
            string filename;

            double volume = volume_slider.Value;

            if (volume == 0)
            {
                filename = @"icons\mute.ico";
            }
            else
            {
                filename = @"icons\volume.ico";
            }

            BitmapImage img = new BitmapImage();
            img.BeginInit();
            img.UriSource = new Uri(filename, UriKind.RelativeOrAbsolute);
            img.EndInit();
            volume_icon.Source = img;
        }
        private void onVolumeClick(object sender, RoutedEventArgs e)
        {

            if (volume_slider.Value > 0)
                volume_slider.Value = 0;
            else
                volume_slider.Value = 50;

            change_volume_icon();
            if (player_id != 0)
            {
                exe.command(player_id, "mute\n");
            }

        }



        private void onVolumeChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {

            double volume = e.NewValue;
            exe.command(player_id, "volume " + volume + " 1\n");

            if (e.OldValue == 0 || e.NewValue == 0)
            {
                change_volume_icon();
            }


        }

        private void onTrimStart(object sender)
        {
            player_progress.SelectionStart = player_progress.Value;


            if (player_progress.SelectionEnd == 0 || player_progress.SelectionEnd <= player_progress.SelectionStart)
            {
                player_progress.SelectionEnd = video.Duration;
            }
            video.trim(player_progress.SelectionStart, player_progress.SelectionEnd);

            CommandUntrim.RaiseCanExecuteChanged();

        }

        private void onTrimEnd(object sender)
        {
            player_progress.SelectionEnd = player_progress.Value;
            if (player_progress.SelectionStart >= player_progress.SelectionEnd)
                player_progress.SelectionStart = 0;
            video.trim(player_progress.SelectionStart, player_progress.SelectionEnd);
            CommandUntrim.RaiseCanExecuteChanged();
        }

        private void onUnTrim(object sender)
        {
            video.untrim();
            player_progress.SelectionStart = 0;
            player_progress.SelectionEnd = 0;
            CommandUntrim.RaiseCanExecuteChanged();

        }
        private void onCrop(object sender)
        {
            //btnCrop.
            if (video.is_cropped())
            {
                video.uncrop();
               // btnCrop.Content = "crop";
                selector.Hide();
            }
            else
            {
               
                selector.Topmost = false;
                selector.Owner = wndPreview;
                selector.ShowInTaskbar = false;

                video.crop(0, 0, 1, 1);

                selector.Region = new Rect(0, 0, 1, 1);
                selector.set_bound(wndPreview.Left, wndPreview.Top, wndPreview.Width, wndPreview.Height);
                selector.Show();

                //btnCrop.Content = "uncrop";
            }
        }

        private void onConvert(object sender)
        {
        }

        public void rectify_width(int w)
        {
            if (video.Width != w)
            {
                Debug.Write("video width rectified.\n");
                video.Width = w;
                adjust_layout();
            }
        }

        public void rectify_height(int h)
        {
            if (video.Height != h)
            {
                Debug.Write("video height rectified.\n");
                video.Height = h;
                adjust_crop();
                adjust_layout();

            }


        }

        public void rectify_duration(double t)
        {
            if (Math.Abs(video.Duration -t) > 1)
            {
                Debug.Write("video duration rectified.\n");
                video.Duration = t;
                adjust_duration();
            }
        }


        public void onRecordStart()
        {
            stop();
            player_id = 0;
            theVideo = recorder.RecordedVideo;
            Status = PlayerStatus.RECORDING;
            DisplayMode = PlayerDisplayMode.MINI;
        }

        public void onRecordEnd()
        {
            Status = PlayerStatus.EMPTY;
            DisplayMode = PlayerDisplayMode.NORMAL;
        }


        public void onRecordProgress(int frame, int sample)
        {
            set_play_progress(recorder.Duration);
        }


        public void onRecordError(int err, string errtext)
        {
        }

        private void onAudioStreamChanged(object sender, SelectionChangedEventArgs e)
        {
            if (as_selector.SelectedValue == null) return;
            video.selected_aid = (int)as_selector.SelectedValue;
            if (video.selected_aid == -1) return;
            if (player_id != 0)
            {
                string cmd = String.Format("set_property switch_audio {0}\n",as_selector.SelectedValue);
                exe.command(player_id,cmd );
            }
        }
        private void onSubStreamChanged(object sender, SelectionChangedEventArgs e)
        {
            if (ts_selector.SelectedValue == null) return;
            video.selected_tid = (int)ts_selector.SelectedValue ;
            if (video.selected_tid == -1) return;
             if (player_id != 0)
            {
                 string cmd = String.Format("set_property sub {0}\n", ts_selector.SelectedValue);
                exe.command(player_id, cmd);
            }

        }
    }



    #region the custom slider
    public class CustomSlider : Slider
    {
        protected override void OnPreviewMouseMove(MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
                RaiseEvent(new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left)
                {
                    RoutedEvent = UIElement.PreviewMouseLeftButtonDownEvent,
                    Source = e.Source,
                });
        }
    }

    #endregion


    #region play callback class

    public class PlayerOutCallback : I_console_callback_t
    {
        VideoPlayer player;


        public PlayerOutCallback(VideoPlayer p)
            : base()
        {
            player = p;
        }

        protected override void on_exit(uint id, IntPtr code)
        {
            try
            {
                player.Dispatcher.BeginInvoke((Action)(() =>
                {
                    player.onPlayerExit(id, code);
                }));
                //throw new NotImplementedException();
            }
            catch (Exception e)
            {
                Debug.Write(e);
            }
        }
        protected override void on_stderr(uint id, IntPtr param, string str)
        {
            try
            {

                Debug.Write("[err=>]" + str);
            }
            catch (Exception e)
            {
                Debug.Write(e);
            }
        }
        protected override void on_stdout(uint id, IntPtr param, string str)
        {
            try
            {
                Debug.Write("[out=>]" + str);
                Match match;
                match = Regex.Match(str,"ID_AUDIO_ID=([0-9]+)");
                if(match.Success){
                    int sid = int.Parse(match.Groups[1].Value);
                    player.Dispatcher.BeginInvoke((Action)(() => { player.theVideo.add_audio_stream(sid); }));
                    return;
                }

                match = Regex.Match(str, "ID_SUBTITLE_ID=([0-9]+)");
                if(match.Success){
                    int sid = int.Parse(match.Groups[1].Value);
                    player.Dispatcher.BeginInvoke((Action)(() => { player.theVideo.add_subtitle_stream(sid); }));
                    return;
                }

                //movie width;
                match = Regex.Match(str, @"ID_VIDEO_WIDTH=([0-9\.]+)", RegexOptions.IgnoreCase);
                if (match.Success)
                {
                    int w = int.Parse(match.Groups[1].Value);
                    player.Dispatcher.BeginInvoke((Action)(() => { player.rectify_width(w); }));
                    return;
                }
                //movie height
                match = Regex.Match(str, @"ID_VIDEO_HEIGHT=([0-9\.]+)", RegexOptions.IgnoreCase);
                if (match.Success)
                {
                    int w = int.Parse(match.Groups[1].Value);
                    player.Dispatcher.BeginInvoke((Action)(() => { player.rectify_height(w); }));

                    return;
                }

                match = Regex.Match(str, @"ID_START_TIME=([0-9\.]+)");
                if (match.Success)
                {
                    double st = double.Parse(match.Groups[1].Value);
                    player.Dispatcher.BeginInvoke((Action)(() => { player.theVideo.StartTime = st; }));
                }

                //movie duration
                match = Regex.Match(str, @"ID_LENGTH=([0-9\.]+)", RegexOptions.IgnoreCase);
                if (match.Success)
                {
                    double t = double.Parse(match.Groups[1].Value);
                    player.Dispatcher.BeginInvoke((Action)(() => { player.rectify_duration(t); }));
                    return;
                }

                //match = Regex.Match(str,@"


                /* match = Regex.Match(str, @"VIDEO:\s*\[.*?\]\s*([0-9]+)x([0-9]+).*", RegexOptions.IgnoreCase);
                 if (match.Success)
                 {
                     int w = int.Parse(match.Groups[1].Value);
                     int h = int.Parse(match.Groups[2].Value);

                     player.Dispatcher.Invoke((Action)(() =>
                     {
                         // outt.set_video_size(w, h);
                     }));
                     return;
                 }*/

                //video progress
                //A:  15.4 V:  15.4 A-V: -0.000 ct: -0.001 385/385  2%  0%  2.7% 0 0          
                // match = Regex.Match(str, @"A:\s*[0-9\.]+\s*V:\s*([0-9\.]+)\s*A_V:.*");
                match = Regex.Match(str, @"V:\s*([0-9]{1,}[.][0-9]*)");
                if (match.Success)
                {
                    double prog = double.Parse(match.Groups[1].Value);
                    player.Dispatcher.BeginInvoke((Action)(() => { player.set_play_progress(prog); }));
                    return;
                }


                //audio progress
                match = Regex.Match(str, @"A:\s*([0-9]{1,}[.][0-9]*)");
                if (match.Success)
                {
                    double prog = double.Parse(match.Groups[1].Value);
                    player.Dispatcher.BeginInvoke((Action)(() => { player.set_play_progress(prog); }));
                    return;
                }

                //screenshot
                match = Regex.Match(str, @"\*\*\*\s+screenshot\s+'(.+)'\s+\*\*\*");
                if (match.Success)
                {
                    string filename = match.Groups[1].Value;
                    player.Dispatcher.BeginInvoke((Action)(() => { player.screen_shot(filename); }));
                    return;
                }


                if (str.IndexOf("=====  PAUSE  =====") >= 0)
                {
                    player.Dispatcher.BeginInvoke((Action)(() => { player.Status = VideoPlayer.PlayerStatus.PAUSED; }));
                }
                // outt.Dispatcher.Invoke((Action)(() => { outt.set_caption(str); }));
            }
            catch (Exception e)
            {
                Debug.Write(e);
            }
        }
    };
    #endregion
}

